package com.twd.allcomponent.redis.utils;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import org.springframework.data.redis.core.Cursor;
import org.springframework.data.redis.core.HashOperations;
import org.springframework.data.redis.core.ListOperations;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ScanOptions;
import org.springframework.data.redis.core.SetOperations;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.data.redis.core.ZSetOperations;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.io.Serializable;
import java.time.Duration;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;

@Component
//    @DependsOn(value = "staticParams")
public class RedisUtils {

    @Resource
    private RedisTemplate redisTemplate;

    private String SYS_KEY_PREFIX = "twd_";


    /**
     * 给key统一加上系统的前缀
     * @param key
     * @return {@link String}
     */
    private String wrapKey(String key) {
        if (key.startsWith(SYS_KEY_PREFIX)) {
            return key;
        }
        return SYS_KEY_PREFIX + key;
    }




    /**
     * 写入缓存
     *
     * @param key
     * @param value
     * @return
     */
    public boolean set(final String key, Object value) {
        boolean result = false;
        try {
            ValueOperations operations = redisTemplate.opsForValue();
            operations.set(key, value);
            result = true;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return result;
    }

    /**
     * 写入缓存设置时效时间
     *
     * @param key
     * @param value
     * @return
     */
    public boolean set(final String key, Object value, Long expireTime, TimeUnit timeUnit) {
        boolean result = false;
        try {
            ValueOperations<Serializable, Object> operations = redisTemplate.opsForValue();
            operations.set(wrapKey(key), value,expireTime,timeUnit);
            result = true;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return result;
    }


    public boolean setNx(final String key, final String value) {
        return 	redisTemplate.opsForValue().setIfAbsent(wrapKey(key),value);
    }

    public boolean setNx(final String key, final String value,long timeout, TimeUnit unit) {
        return 	redisTemplate.opsForValue().setIfAbsent(wrapKey(key),value,timeout,unit);
    }

    /**
     * 批量删除对应的value
     *
     * @param keys
     */
    public void remove(final String... keys) {
        for (String key : keys) {
            remove(wrapKey(key));
        }
    }

    /**
     * 批量删除key
     *
     * @param pattern
     */
    public void removePattern(final String pattern) {
        Set<Serializable> keys = redisTemplate.keys(wrapKey(pattern));
        if (keys.size() > 0) {
            redisTemplate.delete(keys);
        }
    }

    /**
     * 删除对应的value
     *
     * @param key
     */
    public boolean remove(final String key) {

        return 	redisTemplate.delete(wrapKey(key));

    }

    /**
     * 判断缓存中是否有对应的value
     *
     * @param key
     * @return
     */
    public boolean exists(final String key) {
        return redisTemplate.hasKey(wrapKey(key));
    }

    /**
     * 读取缓存
     *
     * @param key
     * @return
     */
    public Object get(final String key) {
        Object result;
        ValueOperations<Serializable, Object> operations = redisTemplate.opsForValue();
        result = operations.get(wrapKey(key));
        return result;
    }

    public <T> T getByType(final String key, Class<T> clz) {

        ValueOperations<Serializable, Object> operations = redisTemplate.opsForValue();
        Object obj = operations.get(wrapKey(key));
        T result = obj==null?null : clz.cast(obj);
        return result;
    }



    public <T> List<T> getList(final String key, Class<T> clz) {
        JSONArray json = getByType(wrapKey(key),JSONArray.class);
        if (json != null) {
            List<T> list = json.toJavaList(clz);
            return list;
        }
        return null;
    }

    /**
     * 哈希 添加
     *
     * @param key
     * @param hashKey
     * @param value
     */
    public void hmSet(String key, Object hashKey, Object value) {
        HashOperations<String, Object, Object> hash = redisTemplate.opsForHash();
        hash.put(wrapKey(key), hashKey, value);
    }

    /**
     * 哈希获取数据
     *
     * @param key
     * @param hashKey
     * @return
     */
    public Object hmGet(String key, Object hashKey) {
        HashOperations<String, Object, Object> hash = redisTemplate.opsForHash();
        return hash.get(wrapKey(key), hashKey);
    }


    /**
     * 哈希删除
     *
     * @param key     关键
     * @param hashKey
     * @return {@link Long}
     */
    public Long hmDelete(String key, String hashKey) {

        HashOperations<String, Object, Object> hash = redisTemplate.opsForHash();
        return hash.delete(wrapKey(key),hashKey);
    }

    public boolean hexists(String key, String hashKey) {

        HashOperations<String, Object, Object> hash = redisTemplate.opsForHash();
        return hash.hasKey(wrapKey(key), hashKey);
    }

    public Map<String, Object> hgetAll(String key) {

        HashOperations<String, String, Object> hash = redisTemplate.opsForHash();
        return hash.entries(wrapKey(key));
    }


    /**
     * 列表添加
     *
     * @param k
     * @param v
     */
    public void lPush(String k, Object v) {
        ListOperations<String, Object> list = redisTemplate.opsForList();
        list.rightPush(wrapKey(k), v);
    }

    /**
     * 列表获取
     *
     * @param k
     * @param l
     * @param l1
     * @return
     */
    public List<Object> lRange(String k, long l, long l1) {
        ListOperations<String, Object> list = redisTemplate.opsForList();
        return list.range(wrapKey(k), l, l1);
    }

    public long rpush(final String key, Object obj) {
        final String value = JSON.toJSONString(obj);
        return redisTemplate.opsForList().rightPush(wrapKey(key), obj);
    }



    public void lset(final String key, long index,Object obj) {
        redisTemplate.opsForList().set(wrapKey(key), index,obj);
    }

    public Long lsize(final String key) {
        return redisTemplate.opsForList().size(wrapKey(key));
    }

    public Object lpop(final String key) {
        return  redisTemplate.opsForList().leftPop(wrapKey(key));
    }

    public Object lpop(final String key, long second) {
        return  redisTemplate.opsForList().leftPop(wrapKey(key), Duration.ofSeconds(second));
    }


    /**
     * 集合添加
     *
     * @param key
     * @param value
     */
    public void sadd(String key, Object value) {
        SetOperations<String, Object> set = redisTemplate.opsForSet();
        set.add(wrapKey(key), value);
    }


    /**
     * 集合长度
     *
     * @param key
     */
    public long scard(String key) {
        SetOperations<String, String> set = redisTemplate.opsForSet();
        return set.size(wrapKey(key));
    }

    /**
     * 集合获取
     *
     * @param key
     * @return
     */
    public Set<Object> smembers(String key) {
        SetOperations<String, Object> set = redisTemplate.opsForSet();
        return set.members(wrapKey(key));
    }

    /**
     * 集合删除
     *
     * @param key
     * @return
     */
    public Long srem(String key,Object... value) {
        SetOperations<String, Object> set = redisTemplate.opsForSet();
        return set.remove(wrapKey(key),value);
    }


    /**
     * 有序集合添加
     *
     * @param key
     * @param value
     * @param scoure
     */
    public void zAdd(String key, Object value, double scoure) {
        ZSetOperations<String, Object> zset = redisTemplate.opsForZSet();
        zset.add(wrapKey(key), value, scoure);
    }

    /**
     * 有序集合获取
     *
     * @param key
     * @param scoure
     * @param scoure
     * @return
     */
    public Set<Object> rangeByScore(String key, double scoure, double scoure1) {
        ZSetOperations<String, Object> zset = redisTemplate.opsForZSet();
        return zset.rangeByScore(wrapKey(key), scoure, scoure1);
    }


    public Long incrBy(String key, int i) {
        return redisTemplate.opsForValue().increment(wrapKey(key), i);
    }

    public void expire(String key, int expire) {
        redisTemplate.expire(wrapKey(key), expire, TimeUnit.SECONDS);
    }


    public List<String> scanKeys(String regex) {
        return (List<String>)  redisTemplate.execute((RedisCallback<List<String>>) connection -> {

            List<String> binaryKeys = new ArrayList<>();

            Cursor<byte[]> cursor = connection.scan(ScanOptions.scanOptions().count(Integer.MAX_VALUE).match(wrapKey(regex)).build());
            while (cursor.hasNext()) {
                binaryKeys.add(new String(cursor.next()));
            }
            cursor.close();
            return binaryKeys;
        });
    }

    public Long ttl(String key) {
        return redisTemplate.getExpire(wrapKey(key));
    }
}


